#include "xToSam1.h"
XOF_TEMPLATEID Templates[MAX_TEMPLATES] = {
	{"template" ,X_TEMPLATE},
   {"FrameTransformMatrix", X_FRAMETRANSFORMMATRIX},
   {"Frame", X_FRAME},
   {"XSkinMeshHeader", X_SKINMESHHEADER},
   {"MeshTextureCoords", X_MESHTEXTURECOORDS},
	{"MeshMaterialList", X_MESHMATERIALLIST},
   {"MeshNormals", X_MESHNORMALS},
   {"Mesh", X_MESH},
   {"Material", X_MATERIAL},
   {"SkinWeights", X_SKINWEIGHTS},
   {"TextureFilename", X_TEXTUREFILENAME},
   {"AnimationSet", X_ANIMATIONSET},
   {"AnimationKey", X_ANIMATIONKEY},
   {"Animation", X_ANIMATION},
	{"Header", X_HEADER}
};

void avoidTemplate(ifstream& fin);
int processBlock(ifstream& fin);
void processMesh(ifstream& fin);
void processBone(ifstream& fin,gewBone* pBone);
void processMeshNormals(ifstream& fin);
void processMeshTextureCoords(ifstream& fin);
void processSkinWeights(ifstream& fin);
void processFrameTransformMatrix(ifstream& fin,gewBone* bone);
void find(ifstream& fin,char c)
{
	while( fin.get()!=c);
}
int vertexNum;
int preVerticesNum;
int normalNum;
int texcoordNum;
arrayVec3 vertices;
arrayVec2 texcoords;
arrayVec3 normals;
arrayVec3 faces;
vector<int> indices;
gewBone *skeleton = 0;
void xToSam1(char* in,char *out)
{
	XFileHeader XHeader;
	cout<<"\nOpen file:"<<in;
	ifstream fin(in, ios::in);
	if (fin.bad())
	{
		cout<<"\nError when open file :"<<in;
   		return;
	}
	fin.read((char*)&XHeader,16);
	if (XHeader.Magic != XOFFILE_FORMAT_MAGIC)
   	{
		cout<<"\nNot a .X model file..";
   		return;
    }
	if (XHeader.Major_Version != XOFFILE_FORMAT_VERSION03)
   	{
		cout<<"\nMajor version greater than 03. Aborted..";
   		return;
    }
	if ((XHeader.Minor_Version != XOFFILE_FORMAT_VERSION03) || (XHeader.Minor_Version != XOFFILE_FORMAT_VERSION02))
   	{
		cout<<"\nMinor version greater than 03. Aborted..";
		return;
    }
	if (XHeader.Format != XOFFILE_FORMAT_TEXT)
   	{
		cout<<"\nNot a text format. Aborted...";
	}

	while(!fin.eof())
   	{
		switch (processBlock(fin)) 
		{
      	case X_ERROR: 
			cout<<"\nError when process this file!.."; 
			return;
        case X_COMMENT: 
			break; //nothing to do
        case X_EBRACE: 
			break; //end of a block ?!
        case X_FRAME: 
			processBone(fin,(gewBone*)0);
			break;
        case X_MESH: 
			processMesh(fin); break;
        /*case X_ANIMATIONSET: 
			ProcessAnimationSets(); break;*/
        case X_OBRACE:
        default: 
			avoidTemplate(fin); break;
      	}
	}
	fin.close();

	// Verify processing result
	int gapSize = vertices.size() - texcoords.size();
	if( gapSize>=0)
	{
		gewVec2 zeroTex(0.,0.);
		for(int i=0;i<gapSize;i++)
			texcoords.push_back(zeroTex);
	}
	else
	{
		cout<<"Error! Number of texcoords is > than number of vertices, something wrong!";
	}
	// Start Convert
	cout<<"\nStart converting..";

	//cout<<"\nStart write output file..";
	//ofstream fout(out,ios_base::out|ios_base::binary| ios_base::trunc);
	//unsigned int temp;
	//// magic number
	//fout<<"ism1";
	//// type of this mesh : 
	////	1 : mesh that contain 3-coord per position, 2-coord per texcoord, 3-coord per normal
	//fout<<char(1);
	//// number type : 
	////  1 : fixed point 16-16
	//fout<<char(1);
	//// vertices count
	//temp = vertices.size();
	//fout.write((char*)(&temp),4);
	//// LOD level number
	//fout<<char(1);
	//// indices length of LOD lv0
	//temp = indices.size();
	//fout.write((char*)(&temp),4);
	//
	//fout.seekp(64,ios_base::beg);
	//// writing vertices attribute as 16-16 fixed point
	//for(int i=0;i<vertices.size();i++)
	//{
	//	// position
	//	temp = floatToFixed( vertices[i].x);
	//	fout.write((char*)(&temp),4);
	//	temp = floatToFixed( vertices[i].y);
	//	fout.write((char*)(&temp),4);
	//	temp = floatToFixed( vertices[i].z);
	//	fout.write((char*)(&temp),4);
	//	// texcoord
	//	temp = floatToFixed( texcoords[i].x);
	//	fout.write((char*)(&temp),4);
	//	temp = floatToFixed( texcoords[i].y);
	//	fout.write((char*)(&temp),4);
	//	// normals
	//	temp = floatToFixed( normals[i].x);
	//	fout.write((char*)(&temp),4);
	//	temp = floatToFixed( normals[i].y);
	//	fout.write((char*)(&temp),4);
	//	temp = floatToFixed( normals[i].z);
	//	fout.write((char*)(&temp),4);			
	//}
	//// writing indices
	//for(int i=0;i<indices.size();i++)
	//{
	//	temp = indices[i];
	//	fout.write((char*)(&temp),4);
	//}
	//cout<<"\nFinish write output file..";
	//fout.close();
}

void avoidTemplate(ifstream& fin)
{
	char Token;
	fin.ignore(TEXT_BUFFER, '{');
	while (!fin.eof())
	{
   		Token = fin.peek();
		if(Token == '{')
      		avoidTemplate(fin);
		if(Token == '}')
      	{
			fin.get();
      		return;
        }
		fin.get();
   	}
}
int blockID(ifstream& fin,string &pText)
{
   long Pos;
   if (fin.eof())
   		return X_COMMENT;
   if (pText.empty())
   {
		cout<<"\nError, no block read !";
   		return X_ERROR;
   }
   for(int i=0; i < MAX_TEMPLATES; i++)
   {
		Pos = pText.find(Templates[i].TxtID);
   		if (Pos > -1)
      	{
			fin.get(); //eats the whitespace after the block name.
   			return Templates[i].TemplateID;
        }
	}
   cout<<"\nUnknown Block:"<<pText;
   return X_UNKNOWN;
}
int processBlock(ifstream& fin)
{
	string Text;
	char Token = fin.peek();
	switch (Token) 
	{
   		case '\n':
   		case ' ':
		case '\t': 
			fin.get(); return X_COMMENT; //spaces are identified as comments
		case '{': 
			return X_OBRACE;
		case '}': 
			fin.get(); return X_EBRACE; //We arrived at the end of the block
		case '/': 
			fin.get(); if (fin.peek() != '/'){return X_ERROR;} //we processed a block name starting with slash ?!
		case '#': 
			fin.ignore(TEXT_BUFFER, '\n'); return X_COMMENT;
		default:
      		fin >> Text;
        return blockID(fin,Text);
   	};
}

void processMesh(ifstream& fin)
{
	string Text;
	int Token;
	char Data[TEXT_BUFFER];
	char name;
	int vNum = 0,fNum=0;
	while(fin.get()!='{');
	fin.getline(Data, TEXT_BUFFER, ';');
	vNum = atoi(Data);
	preVerticesNum = vertices.size();
	for(int i=0; i< vNum; i++)
	{
		gewVec3 vec;
		fin.getline(Data, TEXT_BUFFER, ';');
		vec.x = atof(Data);
		fin.getline(Data, TEXT_BUFFER, ';');
		vec.y = atof(Data);
		fin.getline(Data, TEXT_BUFFER, ';');
		vec.z = atof(Data);
		fin.get();//eats either the comma or the semicolon at the end of each vertex description
		vertices.push_back(vec);
		vertexNum++;
   }

   fin.getline(Data, TEXT_BUFFER, ';');
   fNum = atoi(Data);
   
   for(int i=0; i<fNum; i++)
   {
	   int index;
		fin.getline(Data, TEXT_BUFFER, ';');
		int vertex_per_face = atoi(Data);
		if( vertex_per_face!=3)
			cout<<"\nError! Please triaguage this mesh before convert!..";
		fin.getline(Data, TEXT_BUFFER, ',');
		index = atoi(Data);
		index+= preVerticesNum;
		indices.push_back(index);

		fin.getline(Data, TEXT_BUFFER, ',');
		index = atoi(Data);
		index+= preVerticesNum;
		indices.push_back(index);

		fin.getline(Data, TEXT_BUFFER, ';');
		index = atoi(Data);
		index+= preVerticesNum;
		indices.push_back(index);
		fin.get();
   }

   Token = X_COMMENT;
   while(Token != X_EBRACE)
   	{
		Token = processBlock(fin);
  		switch (Token) 
		{
      		case X_COMMENT: 
				break; //used for spaces and other kind of comments
			case X_EBRACE: 
				return; //this is the end, my only friend ...
			case X_MESHNORMALS: 
				processMeshNormals(fin); 
				break;
      		case X_MESHTEXTURECOORDS: 
				processMeshTextureCoords(fin); 
				break;
			case X_MESHMATERIALLIST: 
				//ProcessMeshMaterials(); 
				avoidTemplate(fin);
				break;
			case X_SKINMESHHEADER: 
				avoidTemplate(fin); 
				break;
		 	case X_SKINWEIGHTS: 
				processSkinWeights(fin); 
				break;
			default:
         	avoidTemplate(fin); break;
		}
   }
}

void processBone(ifstream& fin,gewBone* pBone)
{
   int Token;
   char Data[TEXT_BUFFER];
   char c;
   gewBone* bone = new gewBone();
   
   Token = fin.peek();
   if (Token != '{')
	   fin >> bone->name;
   else
	   bone->name = "Bone_no_name";
   if(!pBone)
   {
	   skeleton = bone;
   }
   else
   {
	   pBone->children.push_back(bone);
   }

   while(fin.get()!='{');
   Token = X_OBRACE;
   while(Token != X_EBRACE)
   {
	Token = processBlock(fin);
    switch (Token) 
	{
     	case X_COMMENT: break; //used for spaces and other kind of comments
        case X_EBRACE: return; //this is the end, my only friend ...
        case X_OBRACE: 
			fin.getline(Data, TEXT_BUFFER, '}'); 
			bone->meshName = Data; 
			break;
     	case X_FRAME: 
			processBone(fin,bone); 
			break;
        case X_FRAMETRANSFORMMATRIX: 
			processFrameTransformMatrix(fin,bone);
			break;
        case X_MESH: 
			processMesh(fin); 
			// Get mesh name?
			break;
        default:
         	avoidTemplate(fin); break;
	}
   }
}

void processMeshNormals(ifstream& fin)
{
	char Data[TEXT_BUFFER];
	find(fin,'{');
	fin.getline(Data, TEXT_BUFFER, ';');
	int nNum = atoi(Data);
	int gapSize = vertices.size() - (normals.size()+nNum);
	if( gapSize != 0)
	{
		cout<<"\nError! Number of vertices != number of normals , still not support auto generate normals yet!";
	}
	for(int i=0; i< nNum; i++)
   	{
		gewVec3 nor;
		fin.getline(Data, TEXT_BUFFER, ';');
		nor.x = atof(Data);
		fin.getline(Data, TEXT_BUFFER, ';');
		nor.y = atof(Data);
		fin.getline(Data, TEXT_BUFFER, ';');
		nor.z = atof(Data);
		fin.get();//eats the comma or the semicolon at the end
		normals.push_back(nor);
    }
	find(fin,'}');
}
void processMeshTextureCoords(ifstream& fin)
{
	char Data[TEXT_BUFFER];
	find(fin,'{');
	fin.getline(Data, TEXT_BUFFER, ';');
	int tNum = atoi(Data);
	int gapSize = vertices.size() - (texcoords.size()+tNum);
	if( gapSize>=0)
	{
		gewVec2 zeroTex(0.,0.);
		for(int i=0;i<gapSize;i++)
			texcoords.push_back(zeroTex);
	}
	else
	{
		cout<<"Error! Number of texcoords is > than number of vertices, something wrong!";
	}
	for(int i=0; i< tNum; i++)
   	{
		gewVec2 tex;
		fin.getline(Data, TEXT_BUFFER, ';');
		tex.x = atof(Data);
		fin.getline(Data, TEXT_BUFFER, ';');
		tex.y = atof(Data);
		fin.get();//eats the comma or the semicolon at the end
		texcoords.push_back(tex);
    }
	find(fin,'}');
}

void processFrameTransformMatrix(ifstream& fin,gewBone* bone)
{
	char Text[TEXT_BUFFER];
	find(fin,'{');
	for(int i=0; i< 15; i++)
   	{
		fin.getline(Text, TEXT_BUFFER, ',');
		bone->matrixPos[i] = atof(Text);
    }
	fin.getline(Text, TEXT_BUFFER, ';');
	bone->matrixPos[15] = atof(Text);
	find(fin,'}');
}

void processSkinWeights(ifstream& fin)
{
	gewBone* bone;
	string temp;
	char Data[TEXT_BUFFER];
	find(fin,'{');
	find(fin,'"');
	fin.getline(Data, TEXT_BUFFER, '"');
	temp = Data;
	bone = skeleton->getBoneName(temp);
	find(fin,';');
	fin.getline(Data, TEXT_BUFFER, ';');
	bone->verticesNum = atoi(Data);
	for(int i=0; i<bone->verticesNum-1 ; i++)
   	{
	   fin.getline(Data, TEXT_BUFFER, ',');
	   bone->vertices.push_back( atoi(Data));
    }
	
	fin.getline(Data, TEXT_BUFFER, ';');
 	bone->vertices.push_back( atoi(Data));

	for(int i=0; i<bone->verticesNum-1 ; i++)
   	{
		fin.getline(Data, TEXT_BUFFER, ',');
		bone->weights.push_back( atof(Data)+preVerticesNum);
    }
    fin.getline(Data, TEXT_BUFFER, ';');
 	bone->weights.push_back( atof(Data));

    for(int i=0; i< 15; i++)
   	{
		fin.getline(Data, TEXT_BUFFER, ',');
		bone->skinOffset[i] = atof(Data)+preVerticesNum;
    }
	fin.getline(Data, TEXT_BUFFER, ';');
	bone->skinOffset[15] = atof(Data);
	find(fin,'}');
}